﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.DataAccess.Common;
using T.FAS.Runtime.DataAccess.Engine;
using T.FAS.Runtime.DataAccess.SPI;
using T.FAS.Runtime.DataAccess.SQLDom;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using T.ServiceComponent.Error;

namespace T.FAS.Runtime.DataAccess.Service
{
    /// <summary>
    /// DataAccess Service Proxy代理类
    /// </summary>
    internal class UnifiedDataAccessServiceProxy : IUnifiedDataAccessService
    {
        /// <summary>
        /// 线程同步对象
        /// </summary>
        private static readonly object s_syncObj = new object();

        /// <summary>
        /// 数据访问服务统一入口
        /// </summary>
        private static IUnifiedDataAccessService s_instance;

        /// <summary>
        /// Real DataAccess Service
        /// </summary>
        private readonly IUnifiedDataAccessService unifiedDataAccessService = UnifiedDataAccessService.GetInstance();

        /// <summary>
        /// Real Engine
        /// </summary>
        public IDataAccessEngine DataAccessEngine
        {
            get
            {
                return unifiedDataAccessService.DataAccessEngine;
            }
            set
            {
                unifiedDataAccessService.DataAccessEngine = value;
            }
        }

        private UnifiedDataAccessServiceProxy()
        {
            DataAccessEngine = Engine.DataAccessEngine.GetInstance();
        }

        /// <summary>
        /// 获取数据访问服务统一入口
        /// </summary>
        /// <returns>数据访问服务统一入口</returns>
        public static IUnifiedDataAccessService GetInstance()
        {
            if (s_instance == null)
            {
                lock (s_syncObj)
                {
                    if (s_instance == null)
                    {
                        s_instance = new UnifiedDataAccessServiceProxy();
                    }
                }
            }

            return s_instance;
        }

        #region Save
        public TResult SaveDataForCDO(CompositeDataObject compositeDataObject, TRecord data, Dictionary<string, object> exrParams = null)
        {
            if (compositeDataObject == null)
                throw new FasException(DataAccessErrorCode.NullValue, nameof(compositeDataObject));
            Preprocess(compositeDataObject);
            if (data == null)
                throw new FasException(DataAccessErrorCode.NullValue, nameof(data));
            return unifiedDataAccessService.SaveDataForCDO(compositeDataObject, data, exrParams);
        }

        public TResult SaveDataForCDO(CompositeDataObject compositeDataObject, string compositeNodeObjectID, TRecord data, Dictionary<string, object> exrParams = null)
        {

            if (compositeDataObject == null)
                throw new FasException(DataAccessErrorCode.NullValue, nameof(compositeDataObject));
            Preprocess(compositeDataObject);
            if (string.IsNullOrWhiteSpace(compositeNodeObjectID))
                throw new FasException(DataAccessErrorCode.NullValue, nameof(compositeNodeObjectID));
            if (data == null)
                throw new FasException(DataAccessErrorCode.NullValue, nameof(data));
            return unifiedDataAccessService.SaveDataForCDO(compositeDataObject, compositeNodeObjectID, data, exrParams);
        }

        public TResult SaveDataForDataObject(DataObject dataObject, TRecord data, Dictionary<string, object> exrParams = null)
        {
            return unifiedDataAccessService.SaveDataForDataObject(dataObject, data, exrParams);
        }

        #endregion

        #region Update
        public TResult UpdateDataForCDO(CompositeDataObject compositeDataObject, TRecord data, Dictionary<string, object> exrParams = null)
        {
            if (compositeDataObject == null)
                throw new FasException(DataAccessErrorCode.NullValue, nameof(compositeDataObject));
            Preprocess(compositeDataObject);
            if (data == null)
                throw new FasException(DataAccessErrorCode.NullValue, nameof(data)); 
            return unifiedDataAccessService.UpdateDataForCDO(compositeDataObject, data, exrParams);
        }

        public TResult UpdateDataForCDO(CompositeDataObject compositeDataObject, string compositeNodeObjectID, TRecord data, Dictionary<string, object> exrParams = null)
        {
            if (compositeDataObject == null)
                throw new FasException(DataAccessErrorCode.NullValue, nameof(compositeDataObject));
            Preprocess(compositeDataObject);
            if (string.IsNullOrWhiteSpace(compositeNodeObjectID))
                throw new FasException(DataAccessErrorCode.NullValue, nameof(compositeNodeObjectID));
            if (data == null)
                throw new FasException(DataAccessErrorCode.NullValue, nameof(data)); 
            return unifiedDataAccessService.UpdateDataForCDO(compositeDataObject, compositeNodeObjectID, data, exrParams);
        }

        public TResult UpdateDataForDataObject(DataObject dataObject, TRecord data, Dictionary<string, object> exrParams = null)
        {
            return unifiedDataAccessService.UpdateDataForDataObject(dataObject, data, exrParams);
        }
        
        public BatchUpdateResult BatchUpdateDataForCDO(CompositeDataObject compositeDataObject, List<TRecord> datas, Dictionary<string, object> exrParams = null)
        {
            return unifiedDataAccessService.BatchUpdateDataForCDO(compositeDataObject, datas, exrParams);
        }
        #endregion

        #region Delete
        public void DeleteDataForCDO(CompositeDataObject compositeDataObject, string dataId, Dictionary<string, object> exrParams = null)
        {
            Preprocess(compositeDataObject);
            unifiedDataAccessService.DeleteDataForCDO(compositeDataObject, dataId, exrParams);
        }

        public void DeleteDataForCDO(CompositeDataObject compositeDataObject, string compositeNodeObjectID, string dataId, Dictionary<string, object> exrParams = null)
        {
            Preprocess(compositeDataObject);
            unifiedDataAccessService.DeleteDataForCDO(compositeDataObject, compositeNodeObjectID, dataId, exrParams);
        }

        public void DeleteDataForDataObject(DataObject dataObject, string dataId, Dictionary<string, object> exrParams = null)
        {
            unifiedDataAccessService.DeleteDataForDataObject(dataObject, dataId, exrParams);
        }

        public void DeleteDataSetForCDO(CompositeDataObject compositeDataObject, IList<string> dataIds, Dictionary<string, object> exrParams = null)
        {
            Preprocess(compositeDataObject);
            unifiedDataAccessService.DeleteDataSetForCDO(compositeDataObject, dataIds, exrParams);
        }

        public void DeleteDataSetForCDO(CompositeDataObject compositeDataObject, string compositeNodeObjectID, IList<string> dataIds, Dictionary<string, object> exrParams = null)
        {
            Preprocess(compositeDataObject);
            unifiedDataAccessService.DeleteDataSetForCDO(compositeDataObject, compositeNodeObjectID, dataIds, exrParams);
        }

        public void DeleteDataSetForDataObject(DataObject dataObject, IList<string> dataIds, Dictionary<string, object> exrParams = null)
        {
            unifiedDataAccessService.DeleteDataSetForDataObject(dataObject, dataIds, exrParams);
        }

        #endregion

        #region Query
        public TResult QueryDataForCDO(CompositeDataObject compositeDataObject, string dataId, Dictionary<string, object> exrParams = null)
        {
            Preprocess(compositeDataObject);
            return unifiedDataAccessService.QueryDataForCDO(compositeDataObject, dataId, exrParams);
        }

        public TResult QueryDataForCDO(CompositeDataObject compositeDataObject, string compositeNodeObjectID, string dataId, Dictionary<string, object> exrParams = null)
        {
            Preprocess(compositeDataObject);
            return unifiedDataAccessService.QueryDataForCDO(compositeDataObject, compositeNodeObjectID, dataId, exrParams);
        }

        public TResult QueryDataForCDO(CompositeDataObject compositeDataObject, QueryFilter queryFilter, Dictionary<string, object> exrParams = null)
        {
            Preprocess(compositeDataObject);
            return unifiedDataAccessService.QueryDataForCDO(compositeDataObject, queryFilter, exrParams);
        }

        public TResult QueryDataForCDO(CompositeDataObject compositeDataObject, string compositeNodeObjectID, QueryFilter queryFilter, Dictionary<string, object> exrParams = null)
        {
            Preprocess(compositeDataObject);
            return unifiedDataAccessService.QueryDataForCDO(compositeDataObject, compositeNodeObjectID, queryFilter, exrParams);
        }

        public TResult QueryDataForDataObject(DataObject dataObject, string dataId, Dictionary<string, object> exrParams = null)
        {
            return unifiedDataAccessService.QueryDataForDataObject(dataObject, dataId, exrParams);
        }

        public TResult QueryDataForDataObject(DataObject dataObject, Dictionary<string, object> parameters, Dictionary<string, object> exrParams = null)
        {
            return unifiedDataAccessService.QueryDataForDataObject(dataObject, parameters, exrParams);
        }

        public TResult QueryDataForDataQuery(DataQuery dataQuery, Dictionary<string, object> parameters, Dictionary<string, object> exrParams = null)
        {
            return unifiedDataAccessService.QueryDataForDataQuery(dataQuery, parameters, exrParams);
        }

        #endregion

        /// <summary>
        /// 预处理CDO元数据
        /// </summary>
        /// <param name="cdo"></param>
        private void Preprocess(CompositeDataObject cdo)
        {
            //在DAC里重构引用会导致每次调用CDO方法都执行，
            //换成由RTP Client维护的内存缓存来搞,只需要从Redis取出并反序列化执行一次的时候执行该逻辑。
            //cdo.BuildModelObjectRelationNew();
        }

        public DataSet Query(FasDBCommand fasDBCommand)
        {
            return unifiedDataAccessService.Query(fasDBCommand);
        }

        public string GetDQActionSQL(DataQuery dataQuery)
        {
            return unifiedDataAccessService.GetDQActionSQL(dataQuery);
        }

        public TRecord RefreshTRecord(CompositeDataObject cdo, TRecord record)
        {
            return unifiedDataAccessService.RefreshTRecord(cdo, record);
        }

        public bool IsHaveStateMachine(CompositeDataObject cdo)
        {
            return unifiedDataAccessService.IsHaveStateMachine(cdo);
        }
    }
}
